package cn.thornbird.orgsync.util;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
public class JsonUtil {

    private static final ObjectMapper objectMapper;

    static {
        objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.setDateFormat(new SimpleDateFormat(DateUtil.DATETIME_FORMAT));
    }

    public static <T> T parseObject(String text, Class<T> clazz) {
        T object = null;
        if (!StringUtils.isEmpty(text)) {
            try {
                object = objectMapper.readValue(text, clazz);
            } catch (JsonProcessingException e) {
                log.error("error", e);
            }
        }
        return object;
    }

    public static <T> T parseObject(String text, TypeReference<T> valueTypeRef) {
        T object = null;
        if (!StringUtils.isEmpty(text)) {
            try {
                object = objectMapper.readValue(text, valueTypeRef);
            } catch (JsonProcessingException e) {
                log.error("error", e);
            }
        }
        return object;
    }

    public static Map<String, Object> parseMap(String text) {
        Map<String, Object> map = new HashMap<>();
        try {
            map = (Map<String, Object>) objectMapper.readValue(text, Map.class);
        } catch (JsonProcessingException e) {
            log.error("error", e);
        }
        return map;
    }

    public static <T> String toJsonString(T object) {
        return toJsonString(object, true);
    }

    public static <T> String toJsonString(T object, boolean includeNullValues) {
        return toJsonString(object, false, includeNullValues);
    }

    public static <T> String toJsonString(T object, boolean pretty, boolean includeNullValues) {
        String text = null;
        objectMapper.setSerializationInclusion(includeNullValues ?
                JsonInclude.Include.ALWAYS : JsonInclude.Include.NON_NULL);
        try {
            if (pretty) {
                text = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
            } else {
                text = objectMapper.writeValueAsString(object);
            }
        } catch (JsonProcessingException e) {
            log.error("error", e);
        }
        return text;
    }

    public static <T> Map<String, Object> toMap(T object) {
        Map<String, Object> map = new HashMap<>();
        String jsonString = toJsonString(object);
        try {
            map = (Map<String, Object>) objectMapper.readValue(jsonString, Map.class);
        } catch (JsonProcessingException e) {
            log.error("error", e);
        }
        return map;
    }

    public static <T> Map<String, Object> toMap(T object, String[] fields) {
        Map<String, Object> map = toMap(object);
        if (fields == null || fields.length == 0) {
            return map;
        }

        String[] keys = map.keySet().toArray(new String[] {});
        for (int i = 0, count = keys.length; i < count; i++) {
            String key = keys[i];
            if (Arrays.stream(fields).filter(field -> field.equals(key)).collect(Collectors.toList()).isEmpty()) {
                map.remove(key);
            } else if (map.get(key) == null) {
                map.remove(key);
            }
        }
        return map;
    }

    public static <T> T cloneObject(T object, Class<T> clazz) {
        return parseObject(toJsonString(object), clazz);
    }

}
